package com.CST.service.impl.ys;

import com.CST.common.BaseEntity;
import com.CST.dto.ys.YsBudgetUpDataDTO;
import com.CST.dto.ys.YsManageBudgetDataDTO;
import com.CST.entity.ys.YsBusinessBudgetData;
import com.CST.entity.ys.YsDepartment;
import com.CST.entity.ys.YsManageBudgetData;
import com.CST.mapper.cst.ys.YsManageBudgetDataMapper;
import com.CST.service.ys.YsManageBudgetDataService;
import com.CST.util.ExcelTransfer;
import com.CST.util.PageHelperTool;
import com.CST.vo.ys.*;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author nmx
 * @since 2023-05-24
 */
@Service
public class YsManageBudgetDataServiceImpl extends ServiceImpl<YsManageBudgetDataMapper, YsManageBudgetData> implements YsManageBudgetDataService {

    private final ExcelTransfer<YsManageBudgetData> excelTransfer;

    public YsManageBudgetDataServiceImpl(ExcelTransfer<YsManageBudgetData> excelTransfer) {
        this.excelTransfer = excelTransfer;
    }

    @Override
    public YsBudgetDataVO listYsManageBudgetDataPage(YsManageBudgetDataDTO dataDTO) {
        Page<YsManageBudgetData> ysManageBudgetDataPage = baseMapper.selectPageNew(dataDTO.getPage(), dataDTO);
        List<String> month = Arrays.asList(new String[]{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"});
        List<String> strings = Optional.ofNullable(month)
                .orElse(Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"));
        if(strings.size()<13){
            strings.add("13");
        }
        List<YsBudgetDataTableInfoVO> tableHeader = new ArrayList<>();
        List<YsBudgetDataInfoVO> data = new ArrayList<>();
        strings.forEach(i -> tableHeader.add(YsBudgetDataTableInfoVO.builder()
                .month(Integer.parseInt(i) < 13 ? i + "月" : "全年")
                .build()));
        setDate(ysManageBudgetDataPage.getRecords(), strings, data);
        Page<YsBudgetDataInfoVO> page = new Page<>();
        page.setRecords(data)
                .setPages(ysManageBudgetDataPage.getPages())
                .setCurrent(ysManageBudgetDataPage.getCurrent())
                .setSize(ysManageBudgetDataPage.getSize())
                .setTotal(ysManageBudgetDataPage.getTotal());
        return YsBudgetDataVO.builder()
                .tableHeader(tableHeader)
                .dataInfo(page)
                .build();
    }

    @Override
    public PageInfo<YsManageBudgetDataVO> getManageList(YsManageBudgetDataDTO dto) {
        List<YsManageBudgetDataVO> list = baseMapper.getManageDataList(dto);
        List<YsManageBudgetDataVO> resultList = new ArrayList<>();
        Map<String,String> map = new HashMap<>();
        list.forEach(s->{
                if (!map.containsKey(String.valueOf(s.getSubjectId()))){
                    map.put(String.valueOf(s.getSubjectId()),String.valueOf(s.getDepartmentId()));
                    map.put(s.getSubjectCode(),s.getDepartmentCode());
                    map.put(s.getSubjectName(),s.getDepartmentName());
                    resultList.add(s);
                } else {
                    String deptIds = map.get(String.valueOf(s.getSubjectId()))+","+s.getDepartmentId();
                    String deptCodes = map.get(s.getSubjectCode())+","+s.getDepartmentCode();
                    String deptNames = map.get(s.getSubjectName())+","+s.getDepartmentName();
                    map.put(String.valueOf(s.getSubjectId()),deptIds);
                    map.put(String.valueOf(s.getSubjectCode()),deptCodes);
                    map.put(String.valueOf(s.getSubjectName()),deptNames);
                }
            }
        );
        resultList.forEach(s->{
            if (map.get(String.valueOf(s.getSubjectId())) != null){
                s.setDepartmentName(map.get(s.getSubjectName()));
                s.setDepartmentCode(map.get(s.getSubjectCode()));
            }
        });
        return PageHelperTool.excutePageInfo(resultList,dto.getPageNum(),dto.getPageSize());
    }

    @Override
    public boolean updateYsManageBudgetData(YsBudgetUpDataDTO dataDTO) {
        YsManageBudgetData ysManageBudgetData = copyData(dataDTO, new YsManageBudgetData(), dataDTO.getMonth());
        return update(ysManageBudgetData, Wrappers.lambdaUpdate(YsManageBudgetData.class)
                .eq(YsManageBudgetData::getYear, dataDTO.getYear())
                .eq(YsManageBudgetData::getSubjectId, dataDTO.getSubjectId()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean importExcelYsManageBudgetData(MultipartFile file,Integer indexYear) {
        try{
            baseMapper.deleteByYear(indexYear);
            List<YsManageBudgetData> ysManageBudgetData = baseMapper.selectManageList();
            Map<String, Integer> map = ysManageBudgetData.stream()
                    .collect(Collectors.toMap(o -> o.getSubjectCode() + o.getSubjectName(),
                            YsManageBudgetData::getSubjectId, (o1, o2) -> o1));
            excelTransfer.importExcel(file, this, YsManageBudgetData.class, list -> {
                ArrayList<YsManageBudgetData> data = new ArrayList<>();
                AtomicReference<Integer> year = new AtomicReference<>();
                list.forEach(e -> {
                    if (year.get() == null) {
                        year.set(Optional.ofNullable(e.getYear())
                                .orElseThrow(() -> new RuntimeException("年份字段为空。")));
                        update(Wrappers.lambdaUpdate(YsManageBudgetData.class)
                                .set(BaseEntity::getDeleteFlag, Boolean.TRUE)
                                .eq(YsManageBudgetData::getYear, year.get()));
                    }
                    boolean b = map.containsKey(e.getSubjectCode() + e.getSubjectName());
                    if (b) {
                        e.setSubjectId(map.get(e.getSubjectCode() + e.getSubjectName()));
                        caclBudgetData(e);
                        data.add(e);
                    }
                });
                return data;
            });
            return true;
        } catch (Exception e){
            throw new RuntimeException("导入失败");
        }
    }

    private YsManageBudgetData caclBudgetData(YsManageBudgetData data) {
        try {
            Class<? extends YsManageBudgetData> clazz = data.getClass();
            // getDeclaredFields  可以获取本类所有的字段,包括private的,但是不能获取继承来的字段
            Field[] declaredFields = clazz.getDeclaredFields();
            // 获取get方法，判断是san 还是 xu + 月份，
            // 循环所有属性，在这里我们只计算 san xu
            for (Field declaredField : declaredFields) {
                String name = declaredField.getName();
                if((name.contains("san") || name.contains("xu")) && name.endsWith("4")){
                    //获取属性名称，除去4，获取类型 1 2 3 的值
                    String subName = name.substring(0, name.length() - 1);
                    String getMethodName  = "get"+subName.substring(0,1).toUpperCase(Locale.ROOT)+subName.substring(1);
                    String setMethodName  = "set"+name.substring(0,1).toUpperCase(Locale.ROOT)+name.substring(1);
                    BigDecimal type1 = (BigDecimal) clazz.getDeclaredMethod(getMethodName+1).invoke(data);
                    BigDecimal type2 = (BigDecimal) clazz.getDeclaredMethod(getMethodName+2).invoke(data);
                    BigDecimal type3 = (BigDecimal) clazz.getDeclaredMethod(getMethodName+3).invoke(data);
                    Method declaredMethod = clazz.getDeclaredMethod(setMethodName, BigDecimal.class);
                    if(type2 != null){
                        declaredMethod.invoke(data,type2.add(type3 == null ? BigDecimal.ZERO:type3));
                    }else{
                        declaredMethod.invoke(data,type1);
                    }
                }
            }
            for (int i = 1; i < 14; i++) {
                for (int j = 1; j < 5; j++){
                    Method setAllMethod = clazz.getDeclaredMethod("setAll" + i + j, BigDecimal.class);
                    BigDecimal sanValue = (BigDecimal) clazz.getDeclaredMethod("getSan"+i+j).invoke(data);
                    BigDecimal xuValue = (BigDecimal) clazz.getDeclaredMethod("getXu"+i+j).invoke(data);
//                    setAllMethod.invoke(data,(sanValue == null ? BigDecimal.ZERO:sanValue).add(xuValue == null ? BigDecimal.ZERO :xuValue));
                }
            }
            return data;
        } catch (Exception e){
            throw new RuntimeException("导入失败");
        }

    }

    @Override
    public void exportExcelYsManageBudgetData(HttpServletResponse response, Integer year) {
        excelTransfer.exportExcel(response, baseMapper.selectNewAll(year)
                        .stream()
                        .peek(this::setDepartment)
                        .collect(Collectors.toList()), "管理编制数据", "sheet",
                YsManageBudgetData.class);
    }

    @Override
    public void exportExcelYsManageBudgetDataDemo(HttpServletResponse response) {
        List<YsManageBudgetData> ysManageBudgetData = baseMapper.selectNew();
        List<YsManageBudgetData> collectList = ysManageBudgetData.stream().peek(this::setDepartment).collect(Collectors.toList());
        excelTransfer.exportExcel(response, collectList, "管理编制数据模板", "sheet",
                YsBusinessBudgetData.class);
    }

    private void setDate(List<YsManageBudgetData> ysBusinessBudgetData, List<String> month, List<YsBudgetDataInfoVO> data) {
        ysBusinessBudgetData.forEach(info -> {
            YsBudgetDataInfoVO vo = new YsBudgetDataInfoVO();
            setDepartment(info);
            vo.setSubjectId(info.getSubjectId())
                    .setDepartmentName(info.getDepartmentName())
                    .setDepartmentCode(info.getDepartmentCode())
                    .setSubjectCode(info.getSubjectCode())
                    .setSubjectName(info.getSubjectName());
            List<YsBudgetDataTableVO> vos = new ArrayList<>();
            month.forEach(m -> vos.add(copyData(info, new YsBudgetDataTableVO(), m)));
            vo.setData(vos);
            data.add(vo);
        });
    }

    @SneakyThrows
    private YsBudgetDataTableVO copyData(YsManageBudgetData source, YsBudgetDataTableVO target, String i) {
        Class<?> sourceClass = source.getClass();
        Class<?> targetClass = target.getClass();
        for (Field field : targetClass.getDeclaredFields()) {
            String name = field.getName();
            char[] charArray = name.toCharArray();
            char c = charArray[charArray.length - 1];
            String string = new StringBuilder(name).deleteCharAt(charArray.length - 1)
                    .append(i)
                    .append(c)
                    .toString();
            String s = Optional.ofNullable(new PropertyDescriptor(string, sourceClass).getReadMethod()
                            .invoke(source))
                    .orElse("")
                    .toString();
            String invoke;
            if (s.length() > 0) {
                invoke = new BigDecimal(s).toString();
            }else {
                invoke = "";
            }
            new PropertyDescriptor(name, targetClass).getWriteMethod()
                    .invoke(target, invoke);
        }
        return target;
    }

    @SneakyThrows
    private YsManageBudgetData copyData(YsBudgetUpDataDTO source, YsManageBudgetData target, String i) {
        Class<?> sourceClass = source.getClass();
        Class<?> targetClass = target.getClass();
        Set<String> set = Arrays.stream(targetClass.getDeclaredFields())
                .map(Field::getName)
                .collect(Collectors.toSet());
        for (Field field : sourceClass.getDeclaredFields()) {
            String name = field.getName();
            char[] charArray = name.toCharArray();
            char c = charArray[charArray.length - 1];
            String string = new StringBuilder(name).deleteCharAt(charArray.length - 1)
                    .append(i)
                    .append(c)
                    .toString();
            if (set.contains(string)) {
                BigDecimal invoke = new BigDecimal(new PropertyDescriptor(name, sourceClass).getReadMethod()
                        .invoke(source)
                        .toString());
                new PropertyDescriptor(string, targetClass).getWriteMethod()
                        .invoke(target, invoke);
            }
        }
        return target;
    }

    private void setDepartment(YsManageBudgetData entity) {
        StringBuilder names = new StringBuilder();
        StringBuilder codes = new StringBuilder();
        List<YsDepartment> departments =
                Optional.ofNullable(entity.getDepartments())
                        .orElse(new ArrayList<>());
        if (departments.isEmpty()) {
            return;
        }
        for (int i = 0; ; i++) {
            YsDepartment ysDepartment = departments.get(i);
            codes.append(ysDepartment.getDepartmentCode());
            names.append(ysDepartment.getDepartmentName());
            if (i == departments.size() - 1) {
                break;
            }
            names.append(", ");
            codes.append(", ");
        }
        entity.setDepartmentName(names.toString());
        entity.setDepartmentCode(codes.toString());
    }
}
